﻿// ===============================================================================
// Repeater.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Mvc.Controls
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using EOS.Framework.Tools.Aspects;
    using System.Globalization;
using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// A control for rendering lists.
    /// </summary>
    /// <typeparam name="TContainer">The type of the container.</typeparam>
    public class Repeater<TContainer> : MarkupControl
    {
        /// <summary>
        /// the delegate, which is called, whenever a new item is handled.
        /// </summary>
        private ElementHandler _elementHandler;
        /// <summary>
        /// The current markup.
        /// </summary>
        private IMarkup _currentMarkup;
        
        #region Properties

        private TContainer _container;

        /// <summary>
        /// Gets the container where this control belongs to.
        /// </summary>
        /// <value>The parent container of this control.</value>
        protected TContainer Container
        {
            get { return _container; }
        }

        private IEnumerable _enumerable;

        /// <summary>
        /// Gets or sets the enumerable, which should be handled by this control.
        /// </summary>
        /// <value>The enumerable to handle. Cannot be null (Nothing in Visual Basic).</value>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is 
        /// null (Nothing in Visual Basic).</exception>
        [NotNull]
        public IEnumerable Enumerable
        {
            get { return _enumerable; }
            set { _enumerable = value; }
        }

        /// <summary>
        /// Gets the current markup of the control.
        /// </summary>
        /// <value>The current markup.</value>
        public override IMarkup Markup
        {
            get { return _currentMarkup; }
        }

        #endregion

        [SuppressMessage("Microsoft.Design", "CA1034")]
        public delegate void ElementHandler(IMarkup body, object element);

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer&gt;"/> class
        /// with the specified container.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException"><paramref name="container"/> is null
        /// (Nothing in Visual Basic).</exception>
        [ParamNotNull("container")]
        public Repeater(TContainer container)
            : this(container, null, null)
        { 
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer&gt;"/> class
        /// with the specified container and the enumeration to handle.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <param name="enumerable">The enumerable to handle. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="container"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="enumerable"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        [ParamNotNull("container"), ParamNotNull("enumerable")]
        public Repeater(TContainer container, IEnumerable enumerable)
            : this(container, enumerable, null)
        { 
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer&gt;"/> class
        /// with the specified container and the enumeration to handle.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <param name="enumerable">The enumerable to handle. Cannot be null (Nothing in Visual Basic).</param>
        /// <param name="elementHandler">The delegate, which will be called for each item. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="container"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="enumerable"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="elementHandler"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        [ParamNotNull("container"), ParamNotNull("enumerable"), ParamNotNull("elementHandler")]
        public Repeater(TContainer container, IEnumerable enumerable, ElementHandler elementHandler)
        {
            this._container = container;
            this._enumerable = enumerable;
            this._elementHandler = elementHandler;
        }

        /// <summary>
        /// Called when the item should be handled
        /// </summary>
        /// <param name="elementIMarkup">The markup for the current item.</param>
        /// <param name="element">The element to handle.</param>
        protected virtual void OnHandleElement(IMarkup elementIMarkup, object element)
        {
            if (_elementHandler != null)
            {
                _elementHandler(_currentMarkup, element);
            }
        }

        /// <summary>
        /// Called when the control should be rendered.
        /// </summary>
        /// <param name="writer">The writer for writing xhtml.</param>
        /// <param name="context">The context object, which contains context values of the current page.</param>
        protected override void OnRender(IXhtmlWriter writer, PageContext context)
        {
            if (_markup == null)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, Properties.Resources.NullProperty, "Markup"));
            }

            if (_enumerable == null)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, Properties.Resources.NullProperty, "Enumerable"));
            }

            foreach (object element in _enumerable)
            {
                WriteBeginAttachedTag(writer);
                _currentMarkup = (IMarkup)_markup.Clone();


                OnHandleElement(_currentMarkup, element);

                _currentMarkup.RenderMarkup(writer, context); 
                WriteEndAttachedTag(writer);
            }

            _currentMarkup = null;
        }
    }

    /// <summary>
    /// A typesafe control for rendering lists.
    /// </summary>
    /// <typeparam name="TContainer">The type of the container.</typeparam>
    /// <typeparam name="TElement">The type of the element.</typeparam>
    public class Repeater<TContainer, TElement> : MarkupControl
    {
        /// <summary>
        /// the delegate, which is called, whenever a new item is handled.
        /// </summary>
        private ElementHandler _elementHandler;
        /// <summary>
        /// The current markup.
        /// </summary>
        private IMarkup _currentMarkup;

        #region Properties

        private TContainer _container;

        /// <summary>
        /// Gets the container where this control belongs to.
        /// </summary>
        /// <value>The parent container of this control.</value>
        protected TContainer Container
        {
            get { return _container; }
        }

        private IEnumerable<TElement> _enumerable;

        /// <summary>
        /// Gets or sets the enumerable, which should be handled by this control.
        /// </summary>
        /// <value>The enumerable to handle. Cannot be null (Nothing in Visual Basic).</value>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is 
        /// null (Nothing in Visual Basic).</exception>
        [NotNull]
        public IEnumerable<TElement> Enumerable
        {
            get { return _enumerable; }
            set { _enumerable = value; }
        }

        /// <summary>
        /// Gets the current markup of the control.
        /// </summary>
        /// <value>The current markup.</value>
        public override IMarkup Markup
        {
            get { return _currentMarkup; }
        }

        #endregion

        [SuppressMessage("Microsoft.Design", "CA1034")]
        public delegate void ElementHandler(IMarkup body, TElement element);

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer,TElement&gt;"/> class
        /// with the specified container.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException"><paramref name="container"/> is null
        /// (Nothing in Visual Basic).</exception>
        [ParamNotNull("container")]
        public Repeater(TContainer container)
            : this(container, null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer,TElement&gt;"/> class
        /// with the specified container and the enumeration to handle.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <param name="enumerable">The enumerable to handle. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="container"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="enumerable"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        [ParamNotNull("container"), ParamNotNull("enumerable")]
        public Repeater(TContainer container, IEnumerable<TElement> enumerable)
            : this(container, enumerable, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Repeater&lt;TContainer,TElement&gt;"/> class
        /// with the specified container and the enumeration to handle.
        /// </summary>
        /// <param name="container">The container, where the repeater belongs to. Cannot
        /// be null (Nothing in Visual Basic).</param>
        /// <param name="enumerable">The enumerable to handle. Cannot be null (Nothing in Visual Basic).</param>
        /// <param name="elementHandler">The delegate, which will be called for each item. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="container"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="enumerable"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="elementHandler"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        [ParamNotNull("container"), ParamNotNull("enumerable"), ParamNotNull("elementHandler")]
        public Repeater(TContainer container, IEnumerable<TElement> enumerable, ElementHandler elementHandler)
        {
            this._container = container;
            this._enumerable = enumerable;
            this._elementHandler = elementHandler;
        }

        /// <summary>
        /// Called when the item should be handled
        /// </summary>
        /// <param name="elementIMarkup">The markup for the current item.</param>
        /// <param name="element">The element to handle.</param>
        protected virtual void OnHandleElement(IMarkup elementIMarkup, TElement element)
        {
            if (_elementHandler != null)
            {
                _elementHandler(_currentMarkup, element);
            }
        }

        /// <summary>
        /// Called when the control should be rendered.
        /// </summary>
        /// <param name="writer">The writer for writing xhtml.</param>
        /// <param name="context">The context object, which contains context values of the current page.</param>
        protected override void OnRender(IXhtmlWriter writer, PageContext context)
        {
            if (_markup == null)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, Properties.Resources.NullProperty, "Markup"));
            }

            if (_enumerable == null)
            {
                throw new InvalidOperationException(
                    string.Format(CultureInfo.CurrentCulture, Properties.Resources.NullProperty, "Enumerable"));
            }

            foreach (TElement element in _enumerable)
            {
                WriteBeginAttachedTag(writer);
                _currentMarkup = (IMarkup)_markup.Clone();


                OnHandleElement(_currentMarkup, element);

                _currentMarkup.RenderMarkup(writer, context);
                WriteEndAttachedTag(writer);
            }

            _currentMarkup = null;
        }
    }
}
